package mathFrame

trait Value {
  def doubleValue: Double
}

case class Number(number: Double) extends Value {
  def doubleValue = number
}

case class Constant(name: String, number: Value) extends Value {
  def doubleValue = number doubleValue
}

case class Variable(name: String, var number: Value = NoValue) extends Value {
  def doubleValue = number doubleValue
}

case class Parameter(name: String) extends Value {
  var pValue: Double = Double NaN
  
  def doubleValue = pValue
}

abstract class MateFunction(val name: String, 
                            val parameters: Value*) extends Value

abstract class LibMateFunction(override val name: String, 
                               val function: (Double*) => Double,
                               override val parameters: Value*)
extends MateFunction(name, parameters: _*) {
  def doubleValue = {
    val values = parameters map {parameter => parameter doubleValue}
    function(values: _*)
  }
}
  
abstract class BinaryOperator(name: Char, 
                        override val function: (Double*) => Double, 
                        override val parameters: Value*) 
extends LibMateFunction(name toString, function, parameters: _*)

abstract class UnaryOperator(name: Char, 
                             override val function: (Double*) => Double, 
                             override val parameters: Value*) 
extends LibMateFunction(name toString, function, parameters: _*)

case class UserFunction1P(override val name: String, 
                     val parameter: Parameter, 
                     val formula: Value) 
extends MateFunction(name, parameter) {
  def doubleValue = formula doubleValue
  
  def apply(p: Double): Double = {
  
    parameter.pValue = p
    doubleValue
  }
  
  def createComposite(composite: Value*): MateFunction = {
    composite.length match {
      case 1 => new CompositeMateFunction1P(this, composite(0))
      case _ => new UserFunction1P("", parameter, new Number(Double NaN))
    }
  }
}

case object NoValue extends UserFunction1P(null, null, null) {
  override def doubleValue = Double NaN
}

case class CompositeMateFunction1P(val formula: UserFunction1P, 
                                   val composite: Value)
extends MateFunction("", formula) {
  def doubleValue = {
    formula.parameter.pValue = composite.doubleValue
    formula.doubleValue
  }
}
  
